<?php

  abstract class JooS_Server_Request_Abstract {
    private static $_paramsForbidden = array("DOCUMENT_ROOT", "SCRIPT_FILENAME", "SCRIPT_NAME", "PHP_SELF");
    
    private $_method = "GET";
    private $_host = null;

    private $_resourceUri = null;
    private $_params = array(), $_headers = array(), $_get = array(), $_post = array(), $_cookie = array(), $_files = array();

    abstract public function __construct();

    abstract public function getRawBody();
    
    abstract public function setRawBody($body);

    protected static function throwException($message, $code = 0) {
      require_once 'JooS/Server/Request/Exception.php';
      throw new JooS_Server_Request_Exception();
    }
    
    public function __get($key) {
      switch (true) {
        case isset($this->_params[$key]):
          return $this->_params[$key];
        case isset($this->_get[$key]):
          return $this->_get[$key];
        case isset($this->_post[$key]):
          return $this->_post[$key];
        case isset($this->_cookie[$key]):
          return $this->_cookie[$key];
        case ($key == "HTTP_HOST"):
          return $this->getHost();
        case ($key == "REQUEST_URI"):
          return $this->getRequestUri();
        case ($key == "QUERY_STRING"):
          return $this->getQueryString();
        case ($key == "HTTP_COOKIE"):
          return $this->getCookieString();
        case ($key == "REQUEST_METHOD"):
          return $this->_method;
        case in_array($key, self::$_paramsForbidden):
          return null;
        case isset($_SERVER[$key]):
          return $_SERVER[$key];
        case isset($_ENV[$key]):
          return $_ENV[$key];
        default:
          return null;
      }
    }
    
    public function __set($key, $value) {
      switch (true) {
        case (in_array($key, self::$_paramsForbidden)):
          break;
        case ($key == "HTTP_HOST"):
          $this->setHost($value);
          break;
        case ($key == "REQUEST_URI"):
          $this->setRequestUri($value);
          break;
        case ($key == "QUERY_STRING"):
          $this->setQueryString($value);
          break;
        case ($key == "HTTP_COOKIE"):
          $this->setCookieString($value);
          break;
        case ($key == "REQUEST_METHOD"):
          $this->setMethod($value);
          break;
        default:
          $this->_params[$key] = $value;
      }
      return $this;
    }

    public function __isset($key) {
      switch (true) {
        case isset($this->_params[$key]):
          return true;
        case isset($this->_get[$key]):
          return true;
        case isset($this->_post[$key]):
          return true;
        case isset($this->_cookie[$key]):
          return true;
        case ($key == "HTTP_HOST"):
          return $this->_host !== null;
        case ($key == "REQUEST_URI"):
          return $this->_resourceUri !== null;
        case in_array($key, array("HTTP_COOKIE", "REQUEST_METHOD", "QUERY_STRING")):
          return true;
        case in_array($key, self::$_paramsForbidden):
          return false;
        case isset($_SERVER[$key]):
          return true;
        case isset($_ENV[$key]):
          return true;
        default:
          return false;
      }
    }

    public function __unset($key) {
      if (isset($this->_params[$key]))
        unset($this->_params[$key]);

      return $this;
    }

    public function setHost($Host) {
      $this->_host = $Host;
      return $this;
    }

    public function getHost() {
      return $this->_host;
    }
    
    public function setMethod($method) {
      $this->_method = $method;
      return $this;
    }

    public function getMethod() {
      return $this->_method;
    }

    public function setRequestUri($requestUri) {
      if (false !== ($pos = strpos($requestUri, '?'))) {
        $query = substr($requestUri, $pos + 1);
        parse_str($query, $vars);
        $this->setQuery($vars);
        $resourceUri = substr($requestUri, 0, $pos);
      }
      else
        $resourceUri = $requestUri;

      $this->_resourceUri = $resourceUri;

      return $this;
    }

    public function getRequestUri() {
      return $this->_resourceUri . (sizeof($this->_get) ? "?" . $this->getQueryString() : "");
    }

    public function getResourceUri() {
      return $this->_resourceUri;
    }

    private $_pathActionPresentationArray = null;
    
    private function _getPathActionPresentationArray() {
      if ($this->_pathActionPresentationArray === null) {
        $Uri = $this->getResourceUri();
        if ($Uri === "/")
          $this->_pathActionPresentationArray = array( "/", "index", "html" );
        else {
          $parts = explode("/", $Uri);
          $file = array_pop($parts);
          array_push($parts, "");
          $Uri = implode("/", $parts);
          $parts = explode(".", $file);
          $this->_pathActionPresentationArray = array($Uri, $parts[0], $parts[1]);
        }
      }
      return $this->_pathActionPresentationArray;
    }

    public function getResourcePath() {
      $requestData = $this->_getPathActionPresentationArray();
      return $requestData[0];
    }

    public function getResourceAction() {
      $requestData = $this->_getPathActionPresentationArray();
      return $requestData[1];
    }
    
    public function getResourcePresentation() {
      $requestData = $this->_getPathActionPresentationArray();
      return $requestData[2];
    }

    private function _setRequestArray($spec, $value, &$array) {
      if ( ((null === $value) && !is_array($spec)) || ((null !== $value) && is_array($spec)) ) {
        self::throwException('Invalid value passed; must be either array of values or key/value pair');
      }
      if ((null === $value) && is_array($spec)) {
        $array = array();
        foreach ($spec as $key => $value) {
          $this->_setRequestArray($key, $value, $array);
        }
        return $this;
      }

      $array[(string) $spec] = $value;
      return $this;
    }
    
    public function setHeaders($spec, $value = null) {
      return $this->_setRequestArray($spec, $value, $this->_headers);
    }

    public function getHeaders() {
      return $this->_headers;
    }

    public function setQuery($spec, $value = null) {
      return $this->_setRequestArray($spec, $value, $this->_get);
    }

    public function getQuery() {
      return $this->_get;
    }

    public function setQueryString($str) {
      $Query = array();
      @parse_str($str, $Query);
      return $this->setQuery($Query);
    }
    
    public function getQueryString() {
      return http_build_query($this->_get);
    }

    public function setPost($spec, $value = null) {
      return $this->_setRequestArray($spec, $value, $this->_post);
    }

    public function getPost() {
      return $this->_post;
    }

    public function setCookie($spec, $value = null) {
      return $this->_setRequestArray($spec, $value, $this->_cookie);
    }

    public function getCookie() {
      return $this->_cookie;
    }

    public function setCookieString($str) {
      // @todo
      self::throwException("setCookieString not implemented yet =(");
    }
    
    public function getCookieString() {
      $ret = array();
      foreach ($this->_cookie as $k => $v)
        $ret[] = "$k=".urlencode($v);
      return implode("; ", $ret);
    }

    public function getRequest() {
      return $this->_params + $this->_cookie + $this->_post + $this->_get;
    }
  }

?>